home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Games of Daze
/
Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso
/
x2ftp
/
msdos
/
progsrc
/
v3dt090
/
3dtools.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1994-11-01
|
21KB
|
1,078 lines
#include <dos.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "3dtools.h"
// 3dtools v0.90ß - released 11-01-94
// C++ 3d engine
// coded by Voltaire/OTM
// all source Copyright (C) 1994 Zach Mortensen
//
// see 3DTOOLS.NFO and OTM-94.NFO for more information
world3d world;
viewPoint camera;
//////
//
// dotProduct function definition
//
//////
int dotProduct(point3d *p1, point3d *p2)
{
return((int) ((p1->rotoX * p2->rotoX) + (p1->rotoY * p2->rotoY) +
(p1->rotoZ * p2->rotoZ)));
}
//////
//
// point3d class definitions
//
//////
point3d::point3d()
{
}
point3d::point3d(long x, long y, long z)
{
setTo(x, y, z);
}
void point3d::save(FILE *fp)
{
int numWritten;
pointRec *temp = new pointRec;
temp->localX = localX;
temp->localY = localY;
temp->localZ = localZ;
numWritten = fwrite(temp, sizeof(pointRec), 1, fp);
}
void point3d::load(FILE *fp)
{
int numRead;
pointRec *temp = new pointRec;
numRead = fread(temp, sizeof(pointRec), 1, fp);
localX = temp->localX;
localY = temp->localY;
localZ = temp->localZ;
rotoX = localX;
rotoY = localY;
rotoZ = localZ;
}
void point3d::setTo(long x, long y, long z)
{
x3d = x;
y3d = y;
z3d = z;
localX = x;
localY = y;
localZ = z;
rotoX = x;
rotoY = y;
rotoZ = z;
oX = 0;
oY = 0;
oZ = 0;
xDeg = 0;
yDeg = 0;
zDeg = 0;
// spherical schtuff
/*
lRho = (long) sqrt(localX * localX + localY * localY + localZ * localZ);
if (localZ == 0)
lTheta = (long) 180 * atan(localX) / PI;
else
lTheta = (long) 180 * atan(localX / localZ) / PI;
if (lRho == 0)
lPhi = 0;
else
lPhi = (long) 180 * acos(localY / lRho) / PI;
gRho = lRho;
gTheta = lTheta;
gPhi = lPhi;
*/
zeroNormal();
xform2d();
}
// project the point to the screen
void point3d::xform2d()
{
if (z3d > 0)
{
x2d = ((x3d << 10) / z3d) + 159;
y2d = 99 - ((y3d << 10) / z3d);
}
else
{
x2d = x3d;
y2d = y3d;
}
}
void point3d::display(int color)
{
setPixel((int) x2d, (int) y2d, color);
}
// set new origin coordinates
void point3d::setNewOrigin(point3d *p)
{
oX = p->x3d;
oY = p->y3d;
oZ = p->z3d;
localX = x3d - oX;
localY = y3d - oY;
localZ = z3d - oZ;
rotoX = localX;
rotoY = localY;
rotoZ = localZ;
}
// set new origin, retaining old local coordinates
void point3d::globalXform2origin(point3d *p)
{
oX = p->x3d;
oY = p->y3d;
oZ = p->z3d;
globalXform();
/*
gRho = (long) sqrt(x3d * x3d + y3d * y3d + z3d * z3d);
if (z3d == 0)
gTheta = (long) 180 * atan(x3d) / PI;
else
gTheta = (long) 180 * atan(x3d / z3d) / PI;
if (gRho == 0)
gPhi = 0;
else
gPhi = (long) 180 * acos(y3d / gRho) / PI;
*/
}
// copy origin coordinates from another point
void point3d::copyOrigin(point3d *p)
{
oX = p->oX;
oY = p->oY;
oZ = p->oZ;
globalXform();
}
/*
void point3d::localRotate(int dTheta, int dPhi)
{
lTheta += dTheta;
lPhi += dPhi;
rotoX = (lRho * sin(lPhi * PI / 180) * cos(lTheta * PI / 180));
rotoY = (lRho * cos(lPhi * PI / 180));
rotoZ = (lRho * sin(lPhi * PI / 180) * sin(lTheta * PI / 180));
globalXform();
}
*/
// rotate a point about its origin (slow method)
void point3d::localRotate(int tX, int tY, int tZ)
{
int cosX, sinX, cosY, sinY, cosZ, sinZ;
xDeg += tX;
yDeg += tY;
zDeg += tZ;
cosX = zDCos(xDeg);
sinX = zDSin(xDeg);
cosY = zDCos(yDeg);
sinY = zDSin(yDeg);
cosZ = zDCos(zDeg);
sinZ = zDSin(zDeg);
// Z axis rotation first
i = (long) ((localX * cosZ - localY * sinZ) >> 16);
j = (long) ((localX * sinZ + localY * cosZ) >> 16);
k = (long) localZ;
// now X axis rotation
rotoY = (long) ((j * cosX - k * sinX) >> 16);
rotoZ = (long) ((j * sinX + k * cosX) >> 16);
k = (long) rotoZ;
// now Y axis
rotoX = (long) ((k * sinY + i * cosY) >> 16);
rotoZ = (long) ((k * cosY - i * sinY) >> 16);
globalXform();
}
// rotate a point about its origin (faster method, *trig array
// used to store sin/cos data that remains constant for all
// points in a given object
void point3d::localRotate(int *trig)
{
// Z axis rotation first
i = ((localX * trig[5] - localY * trig[4]) >> 16);
j = ((localX * trig[4] + localY * trig[5]) >> 16);
k = localZ;
// now X axis rotation
rotoY = ((j * trig[1] - k * trig[0]) >> 16);
rotoZ = ((j * trig[0] + k * trig[1]) >> 16);
k = rotoZ;
// now Y axis
rotoX = ((k * trig[2] + i * trig[3]) >> 16);
rotoZ = ((k * trig[3] - i * trig[2]) >> 16);
globalXform();
}
// rotate a point about THE (0, 0, 0) origin
void point3d::globalRotate(int *trig)
{
// Z axis rotation first
i = (long) ((x3d * trig[5] - y3d * trig[4]) >> 16);
j = (long) ((x3d * trig[4] + y3d * trig[5]) >> 16);
k = (long) z3d;
// now X axis rotation
y3d = (long) ((j * trig[1] - k * trig[0]) >> 16);
z3d = (long) ((j * trig[0] + k * trig[1]) >> 16);
k = (long) z3d;
// now Y axis
x3d = (long) ((k * trig[2] + i * trig[3]) >> 16);
z3d = (long) ((k * trig[3] - i * trig[2]) >> 16);
}
// translate (move) a point by (dX, dY, dZ)
void point3d::translate(long dX, long dY, long dZ)
{
oX += dX;
oY += dY;
oZ += dZ;
globalXform();
}
// transform local coordinates to global (world) coordinates
void point3d::globalXform()
{
x3d = rotoX + oX;
y3d = rotoY + oY;
z3d = rotoZ + oZ;
xform2d();
}
// zero the normal for this point (used for gouraud shading)
void point3d::zeroNormal()
{
nX = 0;
nY = 0;
nZ = 0;
nCount = 0;
}
// add (dX, dY, dZ) to the normal for this point
void point3d::addNormal(int dX, int dY, int dZ)
{
nX += dX;
nY += dY;
nZ += dZ;
nCount ++;
}
// average the normal of this point
void point3d::avgNormal()
{
long d;
if (nCount)
{
nX /= nCount;
nY /= nCount;
nZ /= nCount;
d = (long) sqrt(nX * nX + nY * nY + nZ * nZ);
nX = (32768 * nX) / d;
nY = (32768 * nY) / d;
nZ = (32768 * nZ) / d;
}
}
point3d::~point3d()
{
}
//////
//
// line3d class definitions
//
//////
// NOTE - I don't really know why I included the line3d object,
// I never really use it anyway. Most of it is undoubtedly full
// of bugs...
line3d::line3d(point3d *p1, point3d *p2, int c)
{
point1 = p1;
point2 = p2;
calcVectors();
color = c;
flag = 0;
}
line3d::line3d(long x1, long y1, long z1, long x2, long y2, long z2, int c)
{
point1 = new point3d(x1, y1, z1);
point2 = new point3d(x2, y2, z2);
calcVectors();
color = c;
flag = 1;
}
void line3d::draw()
{
// draw_line(point1->x2d, point1->y2d, point2->x2d, point2->y2d, color);
}
void line3d::calcVectors()
{
i = (point1->x3d - point2->x3d);
j = (point1->y3d - point2->y3d);
k = (point1->z3d - point2->z3d);
}
long line3d::xOfT(double t)
{
return((long) (point1->x3d + (t * i)));
}
long line3d::yOfT(double t)
{
return((long) (point1->y3d + (t * j)));
}
long line3d::zOfT(double t)
{
return((long) (point1->z3d + (t * k)));
}
double line3d::tOfX(long x)
{
return((i == 0 ? 0 : (x - point1->x3d) / i));
}
double line3d::tOfY(long y)
{
return((j == 0 ? 0 : (y - point1->y3d) / j));
}
double line3d::tOfZ(long z)
{
return((k == 0 ? 0 : (z - point1->z3d) / k));
}
void line3d::localRotate(double tX, double tY, double tZ)
{
point1->localRotate(tX, tY, tZ);
point2->localRotate(tX, tY, tZ);
calcVectors();
}
line3d::~line3d()
{
if (flag)
{
delete point1;
delete point2;
}
}
//////
//
// polygon class definitions
//
//////
// These are all triangles BTW
polygon::polygon()
{
}
polygon::polygon(point3d *v1, point3d *v2, point3d *v3, int c)
{
setColor(c);
normal = new point3d;
setTo(v1, v2, v3);
shading = sNone;
facing = fBoth;
}
polygon::polygon(point3d *v1, point3d *v2, point3d *v3, point3d *norm, int c)
{
setColor(c);
p1 = v1;
p2 = v2;
p3 = v3;
normal = norm;
line = new line3d* [3];
line[0] = new line3d(p1, p2, color);
line[1] = new line3d(p2, p3, color);
line[2] = new line3d(p3, p1, color);
}
void polygon::setTo(point3d *v1, point3d *v2, point3d *v3)
{
float i, j, k, d;
p1 = v1;
p2 = v2;
p3 = v3;
i = (float) (((p2->localY - p1->localY) * (p3->localZ - p1->localZ)) -
((p2->localZ - p1->localZ) * (p3->localY - p1->localY)));
j = (float) (((p2->localZ - p1->localZ) * (p3->localX - p1->localX)) -
((p2->localX - p1->localX) * (p3->localZ - p1->localZ)));
k = (float) (((p2->localX - p1->localX) * (p3->localY - p1->localY)) -
((p2->localY - p1->localY) * (p3->localX - p1->localX)));
d = (float) sqrt((double) ((i * i) + (j * j) + (k * k)));
i = (float) (i / d * 32767);
j = (float) (j / d * 32767);
k = (float) (k / d * 32767);
normal->setTo((long) i, (long) j, (long) k);
normal->copyOrigin(p1);
line = new line3d* [3];
line[0] = new line3d(p1, p2, color);
line[1] = new line3d(p2, p3, color);
line[2] = new line3d(p3, p1, color);
}
// average Z value of a polygon, used for depth sorting
long polygon::avgZ()
{
return((long) ((p1->z3d + p2->z3d + p3->z3d) / 3));
}
void polygon::setColor(int c)
{
color = c;
}
void polygon::wireFrame()
{
for (count = 0; count < 3; count++)
line[count]->draw();
}
void polygon::paintSolid()
{
//dot = dotProduct(normal, camera.view);
//if (dot < 0)
if (shading)
dot = -(dotProduct(normal, camera.light));
poly3(p1->x2d, p1->y2d, p2->x2d, p2->y2d, p3->x2d, p3->y2d,
color + (dot >> 11));
// NOTE: dot >> 11 = (dot / 32767 * 16) = cos T * 16 = offset into
// block of 16 colors
}
void polygon::setNewOrigin(point3d *p)
{
p1->setNewOrigin(p);
p2->setNewOrigin(p);
p3->setNewOrigin(p);
normal->setNewOrigin(p);
}
// this function should be called for all polygons in an object,
// following which point->avgNormals() should be called for all
// points in an object. The obj3d->setGNormals function takes
// care of all of this...
void polygon::setGNormals()
{
p1->addNormal(normal->rotoX, normal->rotoY, normal->rotoZ);
p2->addNormal(normal->rotoX, normal->rotoY, normal->rotoZ);
p3->addNormal(normal->rotoX, normal->rotoY, normal->rotoZ);
}
void polygon::gShade()
{
int dot1, dot2, dot3;
//dot = dotProduct(normal, camera.view);
//if (dot < 0)
//{
dot1 = abs(dotProduct((point3d *) p1->normal, camera.light));
dot2 = abs(dotProduct((point3d *) p2->normal, camera.light));
dot3 = abs(dotProduct((point3d *) p3->normal, camera.light));
gpoly3(p1->x2d, p1->y2d, color + (dot1 >> 11),
p2->x2d, p2->y2d, color + (dot2 >> 11),
p3->x2d, p3->y2d, color + (dot3 >> 11));
//}
}
void polygon::setShading(int shade)
{
shading = shade;
}
void polygon::setFacing(int face)
{
facing = face;
}
// display a polygon according to the set shading and facing data
void polygon::display()
{
if (shading)
{
if (facing)
{
dot = dotProduct(normal, camera.view);
if (facing == fInside)
dot = -dot;
if (dot < 0)
{
if (shading == sGouraud)
gShade();
else
paintSolid();
}
}
else
{
if (shading == sGouraud)
gShade();
else
paintSolid();
}
}
else
{
if (facing)
{
dot = dotProduct(normal, camera.view);
if (facing == fInside)
dot = -dot;
if (dot < 0)
{
dot = 0;
paintSolid();
}
}
else
{
dot = 0;
paintSolid();
}
}
}
polygon::~polygon()
{
for (count = 0; count < 3; count++)
delete line[count];
delete line;
delete normal;
}
//////
//
// obj3d class definitions
//
//////
obj3d::obj3d(long x, long y, long z)
{
origin = new point3d(x, y, z);
numPoints = 0;
numPolys = 0;
point = new point3d* [1];
poly = new polygon* [1];
trig = new int [6];
xDeg = 0;
yDeg = 0;
zDeg = 0;
}
// the save and load functions are out of date. If you wish, you may
// rewrite them...
void obj3d::save(FILE *fp)
{
int numWritten, count2;
polyRec *pRec = new polyRec;
objFileHeader *header = new objFileHeader;
// assumes fp has already been opened, so we can store multiple objects
// in the same file
header->numPoints = numPoints;
header->numPolys = numPolys;
numWritten = fwrite(header, sizeof(objFileHeader), 1, fp);
for (count = 0; count < numPoints; count++)
point[count]->save(fp);
for (count2 = 0; count2 < numPolys; count2++)
{
pRec->p1 = getPointNum(poly[count2]->p1);
pRec->p2 = getPointNum(poly[count2]->p2);
pRec->p3 = getPointNum(poly[count2]->p3);
pRec->normal = getPointNum(poly[count2]->normal);
pRec->color = poly[count2]->color;
numWritten = fwrite(pRec, sizeof(polyRec), 1, fp);
}
}
void obj3d::load(FILE *fp)
{
int numRead;
polyRec *pRec = new polyRec;
objFileHeader *header = new objFileHeader;
// free old data
for (count = 0; count < numPoints; count++)
delete point[count];
for (count = 0; count < numPolys; count++)
delete point[count];
// assumes fp has already been opened, so we can store multiple objects
// in the same file
numRead = fread(header, sizeof(objFileHeader), 1, fp);
numPoints = header->numPoints;
numPolys = header->numPolys;
for (count = 0; count < numPoints; count++)
{
point[count] = new point3d;
point[count]->load(fp);
point[count]->globalXform2origin(origin);
}
for (count = 0; count < numPolys; count++)
{
numRead = fread(pRec, sizeof(polyRec), 1, fp);
poly[count] = new polygon(point[pRec->p1], point[pRec->p2],
point[pRec->p3], point[pRec->normal], pRec->color);
}
// nix the old rotations...
xDeg = 0;
yDeg = 0;
zDeg = 0;
}
// add a point whose coordinates are given as local to the origin of
// this object to this objects list of points
void obj3d::addLocalPoint(long x, long y, long z)
{
point3d **temp;
temp = new point3d* [numPoints + 1];
memcpy(temp, point, numPoints * sizeof(point3d*));
delete point;
point = temp;
numPoints++;
point[numPoints - 1] = new point3d(x, y, z);
point[numPoints - 1]->globalXform2origin(origin);
}
void obj3d::addLocalPoint(point3d *p)
{
point3d **temp;
temp = new point3d* [numPoints + 1];
memcpy(temp, point, numPoints * sizeof(point3d*));
delete point;
point = temp;
p->globalXform2origin(origin);
numPoints++;
point[numPoints - 1] = p;
}
// add a point whose coordinates are given in terms of THE (0,0,0)
// origin to this object's list of points
void obj3d::addGlobalPoint(long x, long y, long z)
{
addLocalPoint(x - origin->x3d, y - origin->y3d, z - origin->z3d);
}
void obj3d::addGlobalPoint(point3d *p)
{
p->setNewOrigin(origin);
addLocalPoint(p);
}
// add a local polygon (whose vertices are local points)
void obj3d::addLocalPoly(polygon *pg)
{
polygon **temp;
temp = new polygon* [numPolys + 1];
memcpy(temp, poly, numPolys * sizeof(polygon*));
delete poly;
poly = temp;
numPolys++;
poly[numPolys - 1] = pg;
addLocalPoint(pg->normal);
}
void obj3d::addLocalPoly(int p1, int p2, int p3, int c)
{
polygon *pg = new polygon(point[p1], point[p2], point[p3], c);
addLocalPoly(pg);
}
// add a poly whose vertices are given in global coordinates
void obj3d::addGlobalPoly(polygon *pg)
{
pg->setNewOrigin(origin);
addLocalPoly(pg);
}
int obj3d::getPointNum(long x, long y, long z)
{
for (count = 0; count < numPoints; count++)
if (point[count]->localX == x && point[count]->localY == y &&
point[count]->localZ == z)
return(count);
// couldn't find it, let's CREATE IT!
addLocalPoint(x, y, z);
return(numPoints - 1);
}
int obj3d::getPointNum(point3d *p)
{
for (count = 0; count < numPoints; count++)
if (point[count] == p)
return(count);
return(-1);
}
// rotate this object about it's origin
void obj3d::localRotate(int tX, int tY, int tZ)
{
xDeg += tX;
yDeg += tY;
zDeg += tZ;
xDeg %= 360;
yDeg %= 360;
zDeg %= 360;
trig[0] = zDSin(xDeg);
trig[1] = zDCos(xDeg);
trig[2] = zDSin(yDeg);
trig[3] = zDCos(yDeg);
trig[4] = zDSin(zDeg);
trig[5] = zDCos(zDeg);
for (count = 0; count < numPoints; count++)
point[count]->localRotate(trig);
}
/*
void obj3d::localRotate(int dTheta, int dPhi)
{
for (count = 0; count < numPoints; count++)
point[count]->localRotate(dTheta, dPhi);
}
*/
// translate (move) this object
void obj3d::translate(int dX, int dY, int dZ)
{
origin->translate(dX, dY, dZ);
for (count = 0; count < numPoints; count++)
point[count]->translate(dX, dY, dZ);
}
// rotate this object about THE (0,0,0) origin
void obj3d::globalRotate(int *trig)
{
origin->globalRotate(trig);
for (count = 0; count < numPoints; count++)
point[count]->globalRotate(trig);
}
// depth sort this object's planes
void obj3d::sortPlanes()
{
polygon *temp;
register int pos, index;
for (pos = 0; pos < (numPolys - 1); pos++)
{
index = pos;
for (count = index + 1; count < numPolys; count++)
if (poly[count]->avgZ() > poly[index]->avgZ())
index = count;
if (index != pos)
{
temp = poly[pos];
poly[pos] = poly[index];
poly[index] = temp;
}
}
}
// display as dots
void obj3d::paintDots()
{
for (count = 0; count < numPoints; count++)
point[count]->display(15);
}
// display as a wireframe
void obj3d::wireFrame()
{
sortPlanes();
for (count = 0; count < numPolys; count++)
poly[count]->wireFrame();
}
// display Lambert (flat) shaded
void obj3d::paintSolid()
{
sortPlanes();
for (count = 0; count < numPolys; count++)
poly[count]->paintSolid();
}
// set the location of this object in global coordinates
void obj3d::setLocation(long x, long y, long z)
{
origin->setTo(x, y, z);
for (count = 0; count < numPoints; count++)
point[count]->setNewOrigin(origin);
}
// have this object set up its normal vectors needed for gouraud shading
void obj3d::setGNormals()
{
int temp;
for (count = 0; count < numPolys; count++)
poly[count]->setGNormals();
for (count = 0; count < numPoints; count++)
point[count]->avgNormal();
temp = numPoints;
for (count = 0; count < temp; count++)
if (point[count]->nCount)
{
addLocalPoint(point[count]->nX, point[count]->nY, point[count]->nZ);
point[count]->normal = (void *) point[numPoints - 1];
}
}
// gouraud shade this object
void obj3d::gShade()
{
sortPlanes();
for (count = 0; count < numPolys; count++)
poly[count]->gShade();
}
// universal display function that utilizes each polygon's shading
// and facing data. This allows you to combine gouraud, flat, and
// nonshaded polygons in the same object.
void obj3d::display()
{
sortPlanes();
for (count = 0; count < numPolys; count++)
poly[count]->display();
}
obj3d::~obj3d()
{
for (count = 0; count < numPolys; count++)
delete poly[count];
for (count = 0; count < numPoints; count++)
delete point[count];
delete trig;
delete poly;
delete point;
delete origin;
}
// THE WORLD AND VIEWPOINT CLASSES HAVE NOT BEEN FULLY IMPLEMENTED! If
// you want to use them, rewrite them. You MUST leave the light and view
// vectors in the camera class, though. They are used for shading and
// plane elimination
//////
//
// world3d class definitons
//
//////
world3d::world3d()
{
object = new obj3d* [MAX_OBJS];
numObjs = 0;
}
world3d::~world3d()
{
int count;
for (count = 0; count < numObjs; count++)
delete object[count];
delete object;
}
//////
//
// viewPoint class definitions
//
//////
viewPoint::viewPoint()
{
location = new point3d(0, 0, 0);
light = new point3d(0, 0, 1);
view = new point3d(0, 0, 1);
}
viewPoint::~viewPoint()
{
delete location;
delete light;
delete view;
}